{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:43:42.394948Z",
     "start_time": "2025-06-27T08:43:42.391443Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['c:\\\\Program Files\\\\Python312\\\\python312.zip', 'c:\\\\Program Files\\\\Python312\\\\DLLs', 'c:\\\\Program Files\\\\Python312\\\\Lib', 'c:\\\\Program Files\\\\Python312', '', 'C:\\\\Users\\\\41507\\\\AppData\\\\Roaming\\\\Python\\\\Python312\\\\site-packages', 'C:\\\\Users\\\\41507\\\\AppData\\\\Roaming\\\\Python\\\\Python312\\\\site-packages\\\\win32', 'C:\\\\Users\\\\41507\\\\AppData\\\\Roaming\\\\Python\\\\Python312\\\\site-packages\\\\win32\\\\lib', 'C:\\\\Users\\\\41507\\\\AppData\\\\Roaming\\\\Python\\\\Python312\\\\site-packages\\\\Pythonwin', 'c:\\\\Program Files\\\\Python312\\\\Lib\\\\site-packages']\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "print(sys.path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:43:44.875129Z",
     "start_time": "2025-06-27T08:43:42.395972Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.datasets import fetch_california_housing\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from tqdm.auto import tqdm\n",
    "from torch.utils.data import DataLoader, Dataset\n",
    "from deeplearning_func import EarlyStopping, ModelSaver\n",
    "from deeplearning_func import plot_learning_curves,plot_learning_loss_curves\n",
    "import os\n",
    "import torch.nn.functional as F\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:43:44.894163Z",
     "start_time": "2025-06-27T08:43:44.875129Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练集大小: 13209\n",
      "验证集大小: 3303\n",
      "测试集大小: 4128\n"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "# 加载加利福尼亚房价数据集\n",
    "housing = fetch_california_housing()\n",
    "X = housing.data\n",
    "y = housing.target\n",
    "\n",
    "# 数据拆分：训练集(60%)、验证集(20%)、测试集(20%)\n",
    "X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
    "X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, test_size=0.2, random_state=42)  # 0.25 x 0.8 = 0.2\n",
    "\n",
    "print(f\"训练集大小: {X_train.shape[0]}\")\n",
    "print(f\"验证集大小: {X_val.shape[0]}\")\n",
    "print(f\"测试集大小: {X_test.shape[0]}\")\n",
    "\n",
    "# 数据标准化\n",
    "scaler = StandardScaler()\n",
    "X_train_scaled = scaler.fit_transform(X_train)\n",
    "X_val_scaled = scaler.transform(X_val)\n",
    "X_test_scaled = scaler.transform(X_test)\n",
    "\n",
    "# 自定义数据集类\n",
    "class HousingDataset(Dataset):\n",
    "    def __init__(self, features, targets):\n",
    "        self.features = torch.FloatTensor(features)\n",
    "        self.targets = torch.FloatTensor(targets).view(-1, 1)\n",
    "        \n",
    "    def __len__(self):\n",
    "        return len(self.features) #返回样本数量\n",
    "    \n",
    "    def __getitem__(self, idx): #传入索引，返回对应索引样本的特征和目标\n",
    "        return self.features[idx], self.targets[idx]\n",
    "\n",
    "# 创建数据集实例\n",
    "train_dataset = HousingDataset(X_train_scaled, y_train)\n",
    "val_dataset = HousingDataset(X_val_scaled, y_val)\n",
    "test_dataset = HousingDataset(X_test_scaled, y_test)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 自定义损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:43:44.900679Z",
     "start_time": "2025-06-27T08:43:44.894163Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(0.3750)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def mse_loss(array1, array2):\n",
    "    return ((array1 - array2) ** 2).mean()\n",
    "# 假设 y_true 是真实值的张量，y_pred 是模型预测值的张量\n",
    "y_true = torch.tensor([3, -0.5, 2, 7])\n",
    "y_pred = torch.tensor([2.5, 0.0, 2.0, 8])\n",
    "\n",
    "# 计算 MSE 损失\n",
    "loss = mse_loss(y_pred, y_true)\n",
    "loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 加载数据，构建模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:43:45.694021Z",
     "start_time": "2025-06-27T08:43:44.901683Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RegressionModel(\n",
      "  (layer1): Linear(in_features=8, out_features=30, bias=True)\n",
      "  (activation): ReLU()\n",
      "  (output): Linear(in_features=30, out_features=1, bias=True)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "from torch import optim\n",
    "\n",
    "# 创建数据加载器\n",
    "train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\n",
    "val_loader = DataLoader(val_dataset, batch_size=32)\n",
    "test_loader = DataLoader(test_dataset, batch_size=32)\n",
    "\n",
    "# 定义神经网络模型\n",
    "class RegressionModel(nn.Module):\n",
    "    def __init__(self, input_dim):\n",
    "        super(RegressionModel, self).__init__()\n",
    "        self.layer1 = nn.Linear(input_dim, 30)\n",
    "        self.activation = nn.ReLU()\n",
    "        self.output = nn.Linear(30, 1)\n",
    "        \n",
    "    def forward(self, x):\n",
    "        x = self.activation(self.layer1(x))\n",
    "        x = self.output(x)\n",
    "        return x\n",
    "\n",
    "# 初始化模型、损失函数和优化器\n",
    "input_dim = X_train.shape[1]\n",
    "model = RegressionModel(input_dim)\n",
    "\n",
    "\n",
    "\n",
    "# 自定义MSE损失函数\n",
    "class CustomMSELoss(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(CustomMSELoss, self).__init__()\n",
    "    \n",
    "    def forward(self, predictions, targets):\n",
    "        # 计算预测值和真实值之间的平方差\n",
    "        squared_diff = (predictions - targets) ** 2\n",
    "        # 返回平均损失\n",
    "        return torch.mean(squared_diff)\n",
    "\n",
    "criterion = mse_loss\n",
    "optimizer = optim.Adam(model.parameters())\n",
    "\n",
    "# 打印模型结构\n",
    "print(model)\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:43:45.697802Z",
     "start_time": "2025-06-27T08:43:45.694021Z"
    }
   },
   "outputs": [],
   "source": [
    "# 评估模型\n",
    "def evaluate_regression_model(model, dataloader,  device,criterion):\n",
    "    model.eval()\n",
    "    running_loss = 0.0\n",
    "\n",
    "    \n",
    "    with torch.no_grad():#禁止 autograd 记录计算图，节省显存与算力。\n",
    "        for inputs, targets in dataloader:\n",
    "            inputs, targets = inputs.to(device), targets.to(device)\n",
    "            outputs = model(inputs) #前向计算\n",
    "            loss = criterion(outputs, targets) #计算损失\n",
    "            \n",
    "            running_loss += loss.item() * inputs.size(0)\n",
    "    \n",
    "    return running_loss / len(dataloader.dataset)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:44:06.812481Z",
     "start_time": "2025-06-27T08:43:45.697802Z"
    }
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "4813ac78674940ab81583f4100994eb4",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "train progress:   0%|          | 0/41300 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "早停触发! 最佳验证准确率(如果是回归，这里是损失): 0.3369\n",
      "早停: 已有10轮验证损失没有改善！\n"
     ]
    }
   ],
   "source": [
    "# 定义回归模型训练函数\n",
    "def train_regression_model(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    criterion, \n",
    "    optimizer, \n",
    "    device='cpu', \n",
    "    num_epochs=100, \n",
    "    print_every=10,\n",
    "    eval_step=500,\n",
    "    model_saver=None,\n",
    "    early_stopping=None\n",
    "):\n",
    "    \"\"\"\n",
    "    训练回归模型的函数\n",
    "    \n",
    "    参数:\n",
    "        model: 要训练的模型\n",
    "        train_loader: 训练数据加载器\n",
    "        val_loader: 验证数据加载器\n",
    "        criterion: 损失函数\n",
    "        optimizer: 优化器\n",
    "        device: 训练设备\n",
    "        num_epochs: 训练轮次\n",
    "        print_every: 每多少轮打印一次结果\n",
    "        eval_step: 每多少步评估一次\n",
    "    \n",
    "    返回:\n",
    "        record_dict: 包含训练和验证记录的字典\n",
    "    \"\"\"\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    epoch_val_loss=0\n",
    "    \n",
    "    with tqdm(total=num_epochs * len(train_loader), desc=\"train progress\") as pbar:\n",
    "        for epoch_id in range(num_epochs):\n",
    "            # 训练\n",
    "            model.train()\n",
    "            running_loss = 0.0\n",
    "            \n",
    "            for inputs, targets in train_loader:\n",
    "                inputs, targets = inputs.to(device), targets.to(device)\n",
    "                \n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                \n",
    "                # 模型前向计算\n",
    "                outputs = model(inputs)\n",
    "                \n",
    "                # 计算损失\n",
    "                loss = criterion(outputs, targets)\n",
    "                \n",
    "                # 梯度回传，计算梯度\n",
    "                loss.backward()\n",
    "                \n",
    "                # 更新模型参数\n",
    "                optimizer.step()\n",
    "                \n",
    "                # 更新步骤\n",
    "                global_step += 1\n",
    "            \n",
    "                # 在每个批次后记录训练损失\n",
    "                epoch_train_loss = loss.item()\n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": epoch_train_loss,\n",
    "                    \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # 验证\n",
    "                if global_step % eval_step == 0:\n",
    "                    epoch_val_loss = evaluate_regression_model(model, val_loader, device, criterion)\n",
    "            \n",
    "                    # 记录验证数据\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": epoch_val_loss, \n",
    "                        \"step\": global_step\n",
    "                    })\n",
    "                    # 保存模型权重\n",
    "                    # 如果有模型保存器，保存模型\n",
    "                    if model_saver is not None:\n",
    "                        model_saver(model, -epoch_val_loss, epoch_id)\n",
    "                    \n",
    "                    # 如果有早停器，检查是否应该早停\n",
    "                    if early_stopping is not None:\n",
    "                        early_stopping(-epoch_val_loss)\n",
    "                        if early_stopping.early_stop:\n",
    "                            print(f'早停: 已有{early_stopping.patience}轮验证损失没有改善！')\n",
    "                            return model,record_dict\n",
    "            \n",
    "                # 更新进度条\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"loss\": f\"{epoch_train_loss:.4f}\", \"val_loss\": f\"{epoch_val_loss:.4f},global_step{global_step}\"})\n",
    "    \n",
    "    return model, record_dict\n",
    "\n",
    "\n",
    "# 训练模型\n",
    "# 初始化早停和模型保存对象\n",
    "early_stopping = EarlyStopping(patience=10, verbose=True)\n",
    "model_saver = ModelSaver(save_dir='model_weights')\n",
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
    "model, record_dict = train_regression_model(\n",
    "    model=model,\n",
    "    train_loader=train_loader,\n",
    "    val_loader=val_loader,\n",
    "    criterion=criterion,\n",
    "    optimizer=optimizer,\n",
    "    num_epochs=100,\n",
    "    print_every=10,\n",
    "    eval_step=500,\n",
    "    early_stopping=early_stopping,\n",
    "    model_saver=model_saver,\n",
    "    device=device\n",
    ")\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:44:06.881801Z",
     "start_time": "2025-06-27T08:44:06.812988Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0EAAAHWCAYAAACxAYILAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAal9JREFUeJzt3Qd4FOXWwPGzabsJSYAQOqH3qjQFFFGaIgp2xYINrwo21M/rvVcFG16vvfcuig07CCpFpUjvvfcQSgJJSN3vOe9mlyQESGB3Z7L5/55n2JIlmd13Z3fOnPOecbjdbrcAAAAAQAURZvUKAAAAAEAwEQQBAAAAqFAIggAAAABUKARBAAAAACoUgiAAAAAAFQpBEAAAAIAKhSAIAAAAQIVCEAQAAACgQiEIAgAAAFChEAQBAIr44IMPxOFwyMaNG61eFQAAAoIgCAAAAECFQhAEAAAAoEIhCAIAQETS09OtXgUAQJAQBAEASuW1116TNm3aiNPplDp16sjw4cNl//79RR6zZs0aueSSS6RWrVricrmkXr16cuWVV0pqaqrvMZMnT5YzzjhDqlSpIrGxsdKiRQv517/+Vap1+OSTT6Rr164SExMjVatWlZ49e8qkSZN8P9e5TKNGjTri/zVs2FCuv/76I+Y9TZs2TW6//XapUaOGWdevvvrKd39xb775pvnZ0qVLffetXLlSLr30UklISDDPt3PnzvL9998X+X85OTkyevRoadasmXlMtWrVzPPX1wEAYI0Ii/4uAKAc0cBCd+T79Okjt912m6xatUpef/11mTNnjvz1118SGRkp2dnZ0r9/f8nKypI77rjDBELbtm2TH3/80QRLlStXlmXLlsnAgQOlffv28uijj5qAau3ateZ3HI/+fV2P7t27m/8bFRUls2fPlt9//1369et3Qs9LA6Dq1avLww8/bDJB559/vgnMvvjiCznrrLOKPHbcuHEmCGzbtq25rc+lR48eUrduXfnnP/8plSpVMv9v8ODB8vXXX8tFF13ke+3GjBkjN998swng0tLSZO7cuTJ//nzp27fvCa03AODkEAQBAI5p9+7dZideA40JEyZIWJiniKBly5YyYsQIk5254YYbZPny5bJhwwb58ssvTXbESwMML81+aLCkvycxMbHU66CBkgY+Glhotsa7Dsrtdp/wc9MMzm+//Sbh4eG++y64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3HLLLXLw4EHfz6dOnSpdu3aVSpUqSZUqVaRHjx6yadMm87NFixbJ2WefLXFxcRIfHy+dOnWSuXPnip1EWL0CoUQr4rLyOFcQAACAreVkiDxZJ/h/91/bRaIqleqhl112mdxxxx0yZcoU6d27t7lv7969MnHiRPn5559NQDJgwAB54oknxOl0ykcffSQXXHCBrFq1SurXr39Sq5meni79+/eXbt26yZw5cyQ5OVluvvlmGTFihHzwwQeSm5srgwcPlmHDhslnn30m2dnZ8vfff5t5Rerqq6+WU089VV5//XUJDw+XhQsXSmRkpNgJQZAfRRXk1SiHAwAAwMmoWrWqnHfeeTJ27FhfEPTVV19JYmKiybKEhYVJhw4dfI9/7LHHZPz48fL999+bYOVkjB07Vg4dOmQCK830qFdeecUEWf/9739NQJOamioDBw6UJk2amJ+3atXK9/83b94s999/v7Rs2dLcbtasmdgNQZAfeXsjUA4HAABgY5ExnqyMFX+3DDSjotmW1157zWR7Pv30U7nyyitNAKSZoFGjRslPP/0kO3bsMNmZzMxME4CcrBUrVpgAyxsAKS1309I8zTT17NlTrr/+epMt6tu3r/Tp00cuv/xyqV27tnnsyJEjTebo448/Nj/TrJY3WLIL5gT5EUEQAABAOaBlW1qWFuyloFystDTzouef1EBny5Yt8scff5jASN13330m8/Pkk0+a+7XkrF27dqY0LRjef/99mTlzpnTv3l3GjRsnzZs3l1mzZpmfaXC2bNkyOf/88+X333+X1q1bm3W1E4IgPyIIAgAAgL+4XC65+OKLTQZI5960aNFCOnbsaH72119/mWzMRRddZIKfWrVqycaNG/3yd1u1amWaG+jcIC/9e5qB0nXw0nk/Dz74oMyYMUPatm1ryui8NCi65557ZNKkSeY5aNBkJwRBfhQZRotsAAAA+I9mfjQT9N577/myQN55Nt98843JAGnAMmTIkCM6yZ3M33S5XDJ06FBZunSpac6gTRquvfZa041uw4YNJvjRTJB2hNNAZ82aNSZ40pI8nZOk3eP0Zxo8aXOFwnOG7IA5QX5EJggAAAD+pG2qExISzFwcDXS8nnvuOdMqW8vRtFnCAw88IGlpaX75mzExMfLLL7/IXXfdZVpv6+1LLrnE/E3vz1euXCkffvih7Nmzx8wFGj58uPzj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",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#画图\n",
    "plot_learning_loss_curves(record_dict,sample_step=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-27T08:44:06.903879Z",
     "start_time": "2025-06-27T08:44:06.881801Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试集上的损失为0.3265\n"
     ]
    }
   ],
   "source": [
    "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
    "test_loss=evaluate_regression_model(model,test_loader,device,criterion)\n",
    "print(f\"测试集上的损失为{test_loss:.4f}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
